import popuplib2

from wcs.libs.messages import messages
from wcs.libs.constants import *
from wcs.libs.races.base import RaceBase

__all__ = ["races", "Race"]

class RaceManager(object):
    """
    This manager contains all information regarding all the races currently
    loaded. This should not contain information regarding individual player's
    races as the Cache should hold information about that.
    """
    def __init__(self):
        """
        Default constructor, assign the instance's attributes.
        """
        self.races = {}
        self.raceOrder = []

    def __setitem__(self, raceName, raceObject):
        """
        Executed when this singleton attempts to have attributes assigned in a
        dictionary format. Ensure that a race object is assigned to the races
        dictionary and the race order appended.

        @param string raceName The name of the race being assigned
        @param race raceObject The race instance which the name is referrign to.
        """
        if not self.races.__contains__(raceName):
            if not isinstance(raceObject, RaceBase):
                raise TypeError("raceObject expected type 'Race' but instead" +
                             "received type '%s'" % (type(raceObject).__name__))
            self.races.__setitem__(raceName, raceObject)
            self.raceOrder.append(raceName)

    def __getitem__(self, raceName):
        """
        Return a race given by the race name. If the race doesn't exist then
        the dictionary will throw a KeyError so there's no need to test for
        containment as the higher objects do that.

        @param string raceName The name of the race to return
        @return RaceBase The race object of which raceName referrs
        """
        return self.races.__getitem__(raceName)

    def __delitem__(self, raceName):
        """
        Executed when an attribute is attempted to be removed from the singleton
        via the del <instance>[<attribute>] method. We can assume that a race
        is needed to be removed, so remove it from memory which in turn calls
        their deconstructors.

        @param string raceName The name of the race to remove from memory
        """
        self.races.__delitem__(raceName)
        self.raceOrder.remove(raceName)

    def __iter__(self):
        """
        Executed automatically when we attempt to iterate over the instance.
        Assume we want an iter object for all the race objects stored; pull a
        list in the order that they were added.
        """
        self.iterOrder = [self.races[x] for x in self.raceOrder]
        return self.iterOrder.__iter__()

    def __contains__(self, raceName):
        """
        Executed when testing if an element exists within this object. We can
        assume that we want to test if the string race exists within the races
        dictionary attribute.

        @param string raceName The name of the race to test
        @return bool Whether or not the element exists within the dict attribute
        """
        return self.races.__contains__(raceName)

    def __call__(self, raceName):
        """
        Executed automatically when this instance is is used as a function. We
        use this for backwards compatability; wcs.Race(<racename>) which
        returned the race instance. This in effect simulates that method's
        function as we still return a race instance. This makes it so we don't
        need to have a function, we can just simply create a new variable
        pointing to the same object.

        @param string raceName The name of the race we want to return
        """
        return self.__getitem__(raceName)

    def loadRace(self, raceName, groupNames=None):
        """
        Loads a race given by a name of a race. This will first check for any
        Python race followed by any KeyGroup races. If it doesn't find any it
        will raise an error.

        @param string raceName The name of the race to load.
        """
        folderName   = RACE_PATH.joinpath(raceName)
        pyName       = raceName + ".py"
        if folderName.joinpath(pyName).exists():
            try:
                PythonRace(raceName)
                if groupNames is None:
                    raceGroups.raceOrder.append(raceName)
                else:
                    lastGroup = raceGroups
                    for groupName in groupNames:
                        if groupName in lastGroup.groups:
                            lastGroup = lastGroup.groups[groupName]
                        else:
                            lastGroup = lastGroup.addGroup(groupName)
                    lastGroup.raceOrder.append(raceName)

            except ImportError:
                if self.__contains__(raceName):
                    self.__delitem__(raceName)
        else:
            raise ValueError("Race %s does not exist" % raceName)

    def unloadRace(self, raceName):
        """
        Unloads a race from memory, ensures that all references to the Race
        object are destroyed thus calling the deconstructor.

        @param string raceName The name of the race to unload
        """
        self.__delitem__(raceName)

    def reloadRace(self, raceName):
        """
        Just an alias to load the race followed by an unload. This will just
        ensure that any changes to the race are updated.

        @param string raceName The name of the race to reload
        """
        self.loadRace(raceName)
        self.unloadRace(raceName)
        
races = Race = RaceManager()      

from wcs.libs.races.groups import *
from wcs.libs.races.python import * 